package com.shuigu.ai.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

import com.shuigu.ai.assistant.AiCodeGenTypeRoutingService;
import com.shuigu.ai.assistant.AppNameGenService;
import com.shuigu.ai.factory.AiCodeGenTypeRoutingServiceFactory;
import com.shuigu.ai.factory.AppNameGenFactory;
import com.shuigu.ai.factory.AssistantConfigFactory;
import com.shuigu.ai.constant.AppConstant;
import com.shuigu.ai.core.AiCodeGeneratorFacade;
import com.shuigu.ai.core.builder.VueProjectBuilder;
import com.shuigu.ai.handler.StreamHandlerExecutor;
import com.shuigu.ai.langgraph4j.CodeGenWorkflow;
import com.shuigu.ai.model.enums.CodeGenTypeEnum;
import com.shuigu.ai.model.request.AppAddRequest;
import com.shuigu.ai.model.vo.AppVO;
import com.shuigu.ai.model.vo.UserVO;
import com.shuigu.ai.monitor.MonitorContext;
import com.shuigu.ai.monitor.MonitorContextHolder;
import com.shuigu.ai.service.ScreenshotService;
import com.shuigu.common.core.domain.entity.SysUser;
import com.shuigu.common.utils.DateUtils;
import com.shuigu.common.utils.SecurityUtils;
import com.shuigu.system.service.ISysUserService;
import dev.langchain4j.data.message.TextContent;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.openai.OpenAiChatModel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.shuigu.ai.mapper.AppMapper;
import com.shuigu.ai.domain.App;
import com.shuigu.ai.service.IAppService;
import reactor.core.publisher.Flux;

/**
 * 应用Service业务层处理
 *
 * @author shuigu
 * @date 2025-09-14
 */
@Slf4j
@Service
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements IAppService {
    @Autowired
    private AppMapper appMapper;

    @Resource
    private ISysUserService sysUserService;

    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;

    @Resource
    private StreamHandlerExecutor streamHandlerExecutor;

    @Resource
    private AssistantConfigFactory assistantConfigFactory;

    @Resource
    private VueProjectBuilder vueProjectBuilder;

    @Resource
    private ScreenshotService screenshotService;

    @Resource
    private AppNameGenFactory appNameGenFactory;

    @Resource
    private AiCodeGenTypeRoutingServiceFactory aiCodeGenTypeRoutingServiceFactory;
    /**
     * 查询应用
     *
     * @param id 应用主键
     * @return 应用
     */
    @Override
    public App selectAppById(Long id) {
        return appMapper.selectAppById(id);
    }
    /**
     * 查询应用列表
     *
     * @param app 应用
     * @return 应用
     */
    @Override
    public List<App> selectAppList(App app) {
        return appMapper.selectAppList(app);
    }

    @Override
    public List<AppVO> selectMyAppList(App app) {
        return appMapper.selectMyAppList(app);
    }

    @Override
    public List<AppVO> selectGoodAppList(App app) {
        return appMapper.selectGoodAppList(app);
    }
    /**
     * 新增应用
     *
     * @param app 应用
     * @return 结果
     */
    @Override
    public int insertApp(App app) {
        app.setCreateTime(DateUtils.getNowDate());
        return appMapper.insertApp(app);
    }
    /**
     * 修改应用
     *
     * @param app 应用
     * @return 结果
     */
    @Override
    public int updateApp(App app) {
                app.setUpdateTime(DateUtils.getNowDate());
        return appMapper.updateApp(app);
    }
    /**
     * 批量删除应用
     *
     * @param ids 需要删除的应用主键
     * @return 结果
     */
    @Override
    public int deleteAppByIds(Long[] ids) {
        return appMapper.deleteAppByIds(ids);
    }
    /**
     * 删除应用信息
     *
     * @param id 应用主键
     * @return 结果
     */
    @Override
    public int deleteAppById(Long id) {
        return appMapper.deleteAppById(id);
    }

    @Override
    public Long createApp(AppAddRequest appAddRequest) {
        // 参数校验
        String initPrompt = appAddRequest.getInitPrompt();
        Assert.notBlank(initPrompt, "初始化 prompt 不能为空");
        // 获取当前登录用户
        Long userId = SecurityUtils.getUserId();
        // 构造入库对象
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setCover("https://1297716214-1321727341.cos.ap-guangzhou.myqcloud.com/2025/04/21/88424dee8e5d46ef9e6756b786de2f2a.png");
        app.setUserId(userId);
        // 大模型生成名称
        String appName = appNameGenFactory.createAppNameGenService().genAppName(initPrompt);
        if(appName.length()>12){
            // 截取12个字符
            appName = appName.substring(0, 12);
        }
        app.setAppName(appName);
        // 使用 AI 智能选择代码生成类型
        CodeGenTypeEnum selectedCodeGenType = aiCodeGenTypeRoutingServiceFactory.createAiCodeGenTypeRoutingService().routeCodeGenType(initPrompt);
        app.setCodeGenType(selectedCodeGenType.getValue());
        // 插入数据库
        boolean result = save(app);
        Assert.notNull(result, "创建应用失败");
        log.info("应用创建成功，ID: {}, 类型: {}", app.getId(), selectedCodeGenType.getValue());
        return app.getId();
    }

    @Override
    public AppVO getAppVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtil.copyProperties(app, appVO);
        // 关联查询用户信息
        Long userId = app.getUserId();
        if (userId != null) {
            SysUser user = sysUserService.selectUserById(userId);
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            appVO.setUser(userVO);
        }
        return appVO;
    }

    @Override
    public List<AppVO> getAppVOList(List<App> appList) {
        if (CollUtil.isEmpty(appList)) {
            return new ArrayList<>();
        }
        // 批量获取用户信息，避免 N+1 查询问题
        Set<Long> userIds = appList.stream()
                .map(App::getUserId)
                .collect(Collectors.toSet());
        Map<Long, UserVO> userVOMap = sysUserService.listByIds(userIds).stream()
                .collect(Collectors.toMap(SysUser::getUserId, sysUser ->
                    BeanUtil.copyProperties(sysUser, UserVO.class)
                ));
        return appList.stream().map(app -> {
            AppVO appVO = getAppVO(app);
            UserVO userVO = userVOMap.get(app.getUserId());
            appVO.setUser(userVO);
            return appVO;
        }).collect(Collectors.toList());
    }

    @Override
    public Flux<String> chatToGenCode(Long appId, String message, SysUser loginUser, boolean agent) {
        // 1. 参数校验
        Assert.isFalse(appId == null || appId <= 0,"应用 ID 不能为空");
        Assert.notBlank(message,"用户消息不能为空");
        // 2. 查询应用信息
        App app = this.getById(appId);
        Assert.notNull(app,"应用不存在");
        // 3. 验证用户是否有权限访问该应用，仅本人可以生成代码
        if (!app.getUserId().equals(loginUser.getUserId())) {
            throw new RuntimeException("仅本人可以访问该应用生成代码");
        }
        // 4. 获取应用的代码生成类型
        String codeGenTypeStr = app.getCodeGenType();
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenTypeStr);
        if (codeGenTypeEnum == null) {
            throw new RuntimeException( "不支持的代码生成类型");
        }

        // 5. 通过校验后，添加用户消息到对话历史(只在 Vue 工程下添加)
//        ChatMemory chatMemory = null;
//        if(codeGenTypeEnum == CodeGenTypeEnum.VUE_PROJECT) {
//            ChatMemoryProvider chatMemoryProvider = assistantConfigFactory.getChatMemoryProvider(appId);
//            chatMemory = chatMemoryProvider.get(appId);
//            chatMemory.add(UserMessage.userMessage(TextContent.from(message)));
//        }
        ChatMemoryProvider chatMemoryProvider = assistantConfigFactory.getChatMemoryProvider(appId);
        ChatMemory chatMemory = chatMemoryProvider.get(appId);

        // 6. 设置监控上下文
        MonitorContextHolder.setContext(
                MonitorContext.builder()
                        .userId(loginUser.getUserId().toString())
                        .appId(appId.toString())
                        .build()
        );
        // 6. 调用 AI 生成代码
//        Flux<String> codeStream = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
        // 6. 根据 agent 参数选择生成方式
        Flux<String> codeStream;
        if (agent) {
            // Agent 模式：使用工作流生成代码
            codeStream = new CodeGenWorkflow().executeWorkflowWithFlux(message, appId);
        } else {
            // 传统模式：调用 AI 生成代码（流式）
            codeStream = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
        }

        // 7. 收集 AI 响应内容并在完成后记录到对话历史
        return streamHandlerExecutor.doExecute(codeStream, chatMemory, appId, codeGenTypeEnum)
                .doFinally(signalType -> {
                    // 流结束时清理（无论成功/失败/取消）
                    MonitorContextHolder.clearContext();
                });
    }

    @Override
    public String deployApp(Long appId, SysUser loginUser) {
        // 1. 参数校验
        Assert.isFalse(appId == null || appId <= 0,"应用 ID 不能为空");
        Assert.notNull(loginUser,"用户未登录");
        // 2. 查询应用信息
        App app = getById(appId);
        Assert.notNull(app, "应用不存在");
        // 3. 验证用户是否有权限部署该应用，仅本人可以部署
        if (!app.getUserId().equals(loginUser.getUserId())) {
            throw new RuntimeException("仅本人可以部署该应用");
        }
        // 4. 检查是否已有 deployKey
        String deployKey = app.getDeployKey();
        // 没有则生成 6 位 deployKey（大小写字母 + 数字）
        if (StrUtil.isBlank(deployKey)) {
            deployKey = RandomUtil.randomString(6);
        }
        // 5. 获取代码生成类型，构建源目录路径
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 6. 检查源目录是否存在
        File sourceDir = new File(sourceDirPath);
        if (!sourceDir.exists() || !sourceDir.isDirectory()) {
            throw new RuntimeException("应用代码不存在，请先生成代码");
        }
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenType);
        if (codeGenTypeEnum == CodeGenTypeEnum.VUE_PROJECT) {
//            // Vue 项目需要构建
//            boolean buildSuccess = vueProjectBuilder.buildProject(sourceDirPath);
//            Assert.isTrue(buildSuccess, "Vue 项目构建失败，请检查代码和依赖");
            // 检查 dist 目录是否存在
            File distDir = new File(sourceDirPath, "dist");
            Assert.notNull(distDir, "Vue 项目构建完成但未生成 dist 目录");
            // 将 dist 目录作为部署源
            sourceDir = distDir;
            log.info("Vue 项目构建成功，将部署 dist 目录: {}", distDir.getAbsolutePath());
        }
        // 7. 复制文件到部署目录
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try {
            FileUtil.copyContent(sourceDir, new File(deployDirPath), true);
        } catch (Exception e) {
            throw new RuntimeException("部署失败：" + e.getMessage());
        }
        // 8. 更新应用的 deployKey 和部署时间
        App updateApp = new App();
        updateApp.setId(appId);
        updateApp.setDeployKey(deployKey);
        updateApp.setDeployedTime(new Date());
        boolean updateResult = this.updateById(updateApp);
        Assert.isTrue(updateResult, "更新应用部署信息失败");

        // 10. 构建应用访问 URL
        String appDeployUrl = String.format("%s/%s/", AppConstant.CODE_DEPLOY_HOST, deployKey);
        // 11. 异步生成截图并更新应用封面
        generateAppScreenshotAsync(appId, appDeployUrl);
        return appDeployUrl;

    }

    /**
     * 异步生成应用截图并更新封面
     *
     * @param appId  应用ID
     * @param appUrl 应用访问URL
     */
    @Override
    public void generateAppScreenshotAsync(Long appId, String appUrl) {
        // 使用虚拟线程异步执行
        Thread.startVirtualThread(() -> {
            // 调用截图服务生成截图并上传
            String screenshotUrl = screenshotService.generateAndUploadScreenshot(appUrl);
            // 更新应用封面字段
            App updateApp = new App();
            updateApp.setId(appId);
            updateApp.setCover(screenshotUrl);
            boolean updated = this.updateById(updateApp);
            Assert.isTrue(updated, "更新应用封面字段失败");
        });
    }


}